home *** CD-ROM | disk | FTP | other *** search
/ NetNews Offline 2 / NetNews Offline Volume 2.iso / news / comp / lang / c++-part1 / 7831 < prev    next >
Encoding:
Text File  |  1996-08-05  |  2.9 KB  |  93 lines

  1. Path: camelot.dsccc.com!not-for-mail
  2. From: kcline@sun152.spd.dsccc.com (Kevin Cline)
  3. Newsgroups: comp.lang.pascal.misc,comp.lang.c++,comp.lang.c,comp.lang.pascal.borland
  4. Subject: Re: Tough FACTORIAL math problem...
  5. Date: 19 Feb 1996 11:14:02 -0600
  6. Organization: DSC Communications Corporation Switch Products Division
  7. Message-ID: <4gab4q$bic@sun152.spd.dsccc.com>
  8. References: <4fr8be$ass@news.iconn.net> <4g0giv$94s@sun132.spd.dsccc.com> <DMuJwF.734@cwi.nl>
  9. NNTP-Posting-Host: sun152.spd.dsccc.com
  10.  
  11. In article <DMuJwF.734@cwi.nl>, Dik T. Winter <dik@cwi.nl> wrote:
  12. >[Note: at the end there is a complete explanation of the problem and the
  13. >solution.  At first here follows a flamelet.]
  14. >
  15. >[stuff previousl posted by me deleted]
  16. >But you did it only halfway, see below.
  17.  
  18. Right; I did miss a key point, but my instinct that there was a O(log n)
  19. solution was correct.
  20.  
  21. >bugs in my first posting exposed...
  22. >
  23. >Did you try your function?
  24. >
  25. Well no.
  26.  
  27. >Using only the last k digits of the factorial (excluding trailing zeros)
  28. >and the last l digits of the multiplier will ultimately lead to a
  29. >period.  (The proof is fairly simple.)  But the last non-zero digit of
  30. >the factorial does not show a period; the increasing number of factors 5
  31. >that can occur in a single number prevent this.  So, all such methods
  32. >will fail at some stage.  Taking care of all individual factors 5 solves
  33. >this.
  34. >
  35.  
  36. Sure, but there is a much quicker and easier way to do this.  Thanks for
  37. pointing me down the correct path.  Here is the definitive solution,
  38. which runs in O(log n) time and can compute the last non-zero digit of
  39. the factorial of any number up to INT_MAX in a few milliseconds.  This
  40. code has been check against 1!-20! and a few elements from Dik's table
  41. that were easy to spot, like 900, 901, 950, 951, 1000
  42.  
  43. Basically, the idea is:
  44.  
  45. 27! = (26x27)x(1x2x3x4)x(6x7x8x9)x(11x12x13x14)x(16x17x18x19)x(21x22x23x24)
  46.     x(5x5x5x5x5)x(1x2x3x4x5)
  47.  
  48. Each number in the list moves some number of places through the cycle
  49. 6,2,4,8.  Each set 1-4 or 6-9 moves two places to the right. 
  50. Each 5 moves one place to the left.  The stuff left over after dividing
  51. one 5 from each multiple of 5 is just another factorial, so we can
  52. solve this quickly with a recursive function, as follows:
  53.  
  54. #include <iostream.h>
  55. #include <stdlib.h>
  56.  
  57. // last digits of powers of two
  58. static int t0[] = { 6, 2, 4, 8 };
  59.  
  60. // log base two (mod 5) of 0! - 4!
  61. static int t1[] = { 0, 0, 1, 0, 2 } ;
  62.  
  63. static int f(int i) {
  64.   if (i == 0) return 0;
  65.   return t1[i % 5] + i/5 + f(i/5);
  66. }
  67.  
  68. static int lastNonZeroDigitOfFactorial(int i) {
  69.   if (i < 2) return 1;
  70.   return t0[f(i) % 4];
  71. }
  72.  
  73. int main(int argc, char **argv) {
  74.   if (argc < 2) {
  75.     cout << "usage: " << argv[0] << " n\n";
  76.     exit(1);
  77.   }
  78.  
  79.   int i = atoi(argv[1]);
  80.   if (i < 0) {
  81.     cout << argv[0] << ": non-negative argument required\n";
  82.     exit(1);
  83.   }
  84.   
  85.   cout << "The last non-zero digit of " << i << "! is "
  86.        << lastNonZeroDigitOfFactorial(i) << endl;
  87. }  
  88.  
  89.  
  90.  
  91. -- 
  92. Kevin Cline
  93.